package main

import (
	"github.com/robertkrimen/otto"
	"io/ioutil"
	"encoding/json"
//	 "strconv"
	 "fmt"
//	 "net"
)
var (
	//os_name_list =make([]string,0)
	oslist =make(map[string] osfile)
	vmconfig configx
	VMdevice = make(map[string] *otto.Otto)
	//Oslist
)

type configx struct{
	osdir string
}
type osfile struct{
	bin	 []byte
	osinfo	osinfo
} 
type osinfo struct{
	desc string
	version string
	title	string
	//	cli []int64
	Enable bool

}

func init(){
	vmconfig.osdir="./dll/os/"
}

func (a osfile) Enable(){a.osinfo.Enable=true}
func (a osfile) Disable(){a.osinfo.Enable=false}

func Interface_load(){
	file_:=getFileList(vmconfig.osdir);
	//oslist=make([]osfile,len(file_))
	for i:=0;i<len(file_);i++{
		Reload_OS(Get_file_name(file_[i]))}
}
func getFileList(path string) []string{
	fs,_:= ioutil.ReadDir(path)
	a:=make([]string,0)
    for _,file:=range fs{
        if file.IsDir(){
            //fmt.Println(path+file.Name())
            //getFileList(path+file.Name()+"/")
        }else{
			//fmt.Println(path+file.Name())
			a=append(a,file.Name())
        }
	}
	return a
}
func Reload_OS(name string)bool{
	_,ok:=oslist[name]
	if(ok){delete(oslist,name)}
	var t osfile
	vm := otto.New()
	Setfunction(vm)
	//t.name=file_[i]
	
	bin,_:=ioutil.ReadFile(vmconfig.osdir+name+".js")
	t.bin=(bin)
	if(t.bin==nil){}
	_, err := vm.Run(t.bin)
	
	if(err !=nil){Print_(console_type_error,"load os package " + name+".js error:"+err.Error());return false}else{
		json_,err:=vm.Call("Getinfo",nil)
		if(err !=nil){Print_(console_type_error,"["+name+".js] "+"no have Getinfo()");return false}
		var osinfo osinfo
		m := make(map[string]string)
		k,_:=json_.ToString()
		err = json.Unmarshal([]byte(k), &m)
		if(err==nil){
			if(m["describe"]==""){osinfo.desc=""}else{osinfo.desc=m["describe"]}
			if(m["version"]==""){osinfo.version="1.0.0.0"}else{osinfo.version=m["version"]}
			if(m["title"]==""){osinfo.title="No name"}else{osinfo.title =m["title"]}
			if(m["Enable"]==""){osinfo.Enable=true}else{osinfo.Enable=m["Enable"]=="1"}
			t.osinfo=osinfo
			//oslist=append(oslist,t)
			//fmt.Printf("%+v\n", t)
			oslist[name]=t
			//os_name_list=append(os_name_list,name)
			return true
		}else{
			//fmt.Printf("%+v\n", err)
			Print_(console_type_error,"["+name+".js] "+"Getinfo() return value is error");return false
		}
		}
		//return false
}
func Console_Get_os_list()([]string,[]string,[]int,[]bool){
	var n =make([]string,0)
	var size=make([]int,0) 
	var v =make([]string,0)
	var e =make([]bool,0)
	//fmt.Printf("%+v\n%+v\n", os_name_list,oslist)
	for /*i:=0;i<len(os_name_list);i++*/name,value:=range oslist{
		//name:=os_name_list[i]
		//value:=oslist[name]
		n=append(n,name)
		size=append(size,len(value.bin))
		v=append(v,value.osinfo.version)
		e=append(e,value.osinfo.Enable)
		//Print_(0,"test_load_"+name)
	}
	return n,v,size,e
	
}
//func Unload_OS(name string)bool{}
func Reload_OS_All(){oslist =make(map[string] osfile);Interface_load()}
func CreatVM(UID string,sock int64,osname string)bool{
	_,ok :=VMdevice[UID]
	
	if(!ok){VMdevice[UID]=otto.New()}//如果已经启动过，则保留自定义参数，否则创建一个新的环境
	_,ok=oslist[osname]
	if !ok{return false}
	_,err:=VMdevice[UID].Run(oslist[osname].bin)//加载系统环境
	if(err!=nil){Interface_Delete_VM(UID);return false}
	//加载交互函数
	VMdevice[UID].Set("UID", UID)
	VMdevice[UID].Set("name", osname)
	t:=fmt.Sprintf("%d",sock)
	VMdevice[UID].Set("sock",t)
	Setfunction(VMdevice[UID])
	//加载完成
	return true}
func Interface_VM_exist(UID string)bool{_,ok :=VMdevice[UID];return ok}
func Interface_OnMessage(UID string,lx int,data []byte,sock int64){
	if(!Interface_VM_exist(UID)){return}
	//_,err:=VMdevice[UID].Run("Onmessage('"+(string)(data)+"'")
	//Print_(0,"Call data"+string(data))
	//v,_:=VMdevice[UID].ToValue(string(data))
	_,err:=VMdevice[UID].Call("Onmessage",nil,ToString(lx),data)
	if(err!=nil){Print_(console_type_error,err.Error())}}
	
func Interface_Delete_VM(UID string){/*VMdevice[UID]*/if(Interface_VM_exist(UID)){delete(VMdevice,UID)}}
func Interface_console(UID string)func(interface {})(otto.Value, error){
	if(Interface_VM_exist(UID)){
	return VMdevice[UID].Run
	//cannot use VMdevice[UID].Run (type func(interface {}) (otto.Value, error)) as type func(interface {}) in return argument
	}
	return nil
}
func unload_OS(osname string){
	delete(oslist,osname)
}
func Interface_Reset(UID string)bool{
	if(!Interface_VM_exist(UID)){return false}
	name,err:=VMdevice[UID].Get("name")
	if(err!=nil){return false}
	sock,err:=VMdevice[UID].Get("sock")
	if(err!=nil){return false}
	Interface_Delete_VM(UID);
	s,_:=sock.ToInteger()
	n,_:=name.ToString()
	return CreatVM(UID,s,n)}
func Setfunction(vm *otto.Otto){
	vm.Set("send",func(call otto.FunctionCall) otto.Value {
		UID, _ := call.Argument(0).ToString()
		sock_,_ :=call.Argument(2).ToString()
		sock:=ToInt64(sock_)
		var err error
		var conn *sockc
		Print_(console_type_debug,"prepare send "+UID)
		if(!(UID=="undefined"||UID=="")){if(len(UID)!=16){return otto.Value{}} 
		conn,err=getsock(UID_get_sock(UID))}else{
			conn,err=getsock(sock)}
		if(err !=nil){Print_(console_type_debug,"[error ] select to "+UID+" :"+err.Error());return otto.Value{}}
		data_,err :=call.Argument(1).ToString()
		if(err!=nil){Print_(console_type_debug,"[error ] send to "+UID+" :"+err.Error());return otto.Value{}}
		Print_(console_type_debug,"send to "+UID+" lx:"+ToString(int(data_[0])))
		_,err=conn.Write(([]byte)(data_))
		if(err!=nil){Print_(console_type_debug,"[error 1 ] send to "+UID+" :"+err.Error());return otto.Value{}}
		return otto.Value{}
	})
	vm.Set("Getuion_Oline",func(call otto.FunctionCall)otto.Value {
		/*UID, _ := call.Argument(0).ToString()
		list,_:=get_machine_Relate_online_user_sock(UID)*/
		return otto.Value{}
	})
	vm.Set("Getuion_sock_Oline",func(call otto.FunctionCall)otto.Value {
		UID, _ := call.Argument(0).ToString()
		list,_:=get_machine_Relate_online_user_sock(UID)
		result,_:=vm.ToValue(list)
		return result
		//return otto.Value{}
	})
	vm.Set("Getuion_all",func(call otto.FunctionCall)otto.Value {
		name, _ := call.Argument(0).ToString()
		list,_:=get_machine_Relate_user(name)
		result,_:=vm.ToValue(list)
		return result
		//return otto.Value{}
	})
	vm.Set("Print",func(call otto.FunctionCall)otto.Value {
		lx,_:=call.Argument(0).ToInteger()
		avg:=make([]string,0)
		for i:=1;i<100;i++{
			t,err:=call.Argument(i).ToString()
			if(err !=nil || t=="undefined"){break}
			avg=append(avg,t)
		}
		Print_((int)(lx),avg...)
		return otto.Value{}
	})
	vm.Set("ExecSQL",func(call otto.FunctionCall)otto.Value{
		sql,_:=call.Argument(0).ToString()
		//if(sql=="")
		a,_:=Exec_SQL(sql)
		result,_:=vm.ToValue(a)
		return result
		//return otto.Value{}
	})
	vm.Set("file_ReadLine",func(call otto.FunctionCall)otto.Value {
		file_,_:=call.Argument(0).ToString()
		line,_:=call.Argument(0).ToInteger()
		x:=(int)(line)
		v,_:=vm.ToValue(file_ReadLine(file_,x))
		return v
		//return otto.Value{}
	})
	vm.Set("Ping",func(call otto.FunctionCall)otto.Value {
		UID,_:=call.Argument(0).ToString()
		timeout,_:=call.Argument(1).ToInteger()
		if(timeout<1000){timeout=1000}
		if(len(UID)!=16){return toV(vm,"[Error] "+"UID Len is < 16 ,must > 16")}
		t:=Ping_(UID,nil,int(timeout))
		
		return toV(vm,ToString(t))
	})
	vm.Set("exec",func(call otto.FunctionCall)otto.Value {
		data,_:=call.Argument(0).ToString()
		sock,_:=vm.Get("sock")
		s,_:=sock.ToInteger()
		conn,err:=getsock(s)
		//Print_(console_type_debug,"enter Exec")
		if(err!=nil){
			Print_(console_type_debug,"Exec err "+err.Error()) 
		}else{
			//Print_(console_type_debug,"Exec code"+data)
			Print_(console_type_debug,"clientid:"+ToString(conn.id))
			conn.Send(254,[]byte(data))
		}
		return otto.Value{}
		//return toV(vm,ToString(t))
	})
	vm.Set("Updata",func(call otto.FunctionCall)otto.Value {
		data,_:=call.Argument(0).ToString()
		_name,_:=vm.Get("UID")
		name,_:=_name.ToString()
		if(len(name)!=16){Print_(console_type_debug,"error :"+name);return otto.Value{}}
		SQL:="update online_temp set tempdata=\""+data+"\" where name=\""+name+"\""
		_,err:=Exec_SQL(SQL)
		if(err!=nil){Print_(console_type_debug,err.Error())}else{Print_(console_type_debug,"SQL:"+SQL)}
		
		return otto.Value{}
	})
	vm.Set("Sleep",func(call otto.FunctionCall)otto.Value {
		time,_:=call.Argument(0).ToInteger()
		Sleep(time)
		return otto.Value{}
	})
	vm.Set("Pop",func(call otto.FunctionCall)otto.Value {
		data,_:=call.Argument(0).ToString()
		sock,_:=vm.Get("sock")
		s,_:=sock.ToInteger()
		conn,err:=getsock(s)
		//Print_(console_type_debug,"enter Exec")
		if(err!=nil){
			Print_(console_type_debug,"Exec err "+err.Error()) 
		}else{
			//Print_(console_type_debug,"Exec code"+data)
			Print_(console_type_debug,"clientid:"+ToString(conn.id))
			conn.Send(6,[]byte(data))
		}
		return otto.Value{}
	})
	
	vm.Run(`function len(v){
		s=typeof(v) 
		if(s=="string"){return s.length}
		else if(s=="object"){return Object.keys(v).length}
		else {return 1}
		}
		send=exec
		/*function exec(data){send(UID,"\254"+data);}*/
		`)
}
func toV(vm *otto.Otto,a string)otto.Value{result,_:=vm.ToValue(a);return result}
